package org.ceteca.explica.core.util;

import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.ceteca.explica.client.ClientContext;
import org.ceteca.explica.client.gui.ImageUtils;
import org.ceteca.explica.core.InterfaceErrors;
import org.ceteca.explica.core.InterfaceExplica;
import org.ceteca.explica.core.partners.AddressOT;
import org.eclipse.swt.graphics.Image;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class GoogleMap {
	private Point2D coordinates;
	private int mapZoom;
	private Dimension mapSize;
	private Image mapImage;
	private AddressOT mapAddress;
	private URL mapURL;
	
	
	
	public GoogleMap() {
		super();
		mapZoom=13;
		mapSize=new Dimension(400,400);
		mapAddress=null;
	}





	public GoogleMap(AddressOT address, int zoom, Dimension size) {
		super();
		this.mapAddress=address;
		this.mapZoom=zoom;
		this.mapSize=size;
		
	}
	
	
	
	
	
	public AddressOT getAddress() {
		return mapAddress;
	}





	public void setAddress(AddressOT address) {
		this.mapAddress = address;
	}





	public Image getMapImage() {
		
		return mapImage;
	}
	public void setMapImage(Image mapImage) {
		this.mapImage = mapImage;
	}
	public Point2D getCoordinates() {
		return coordinates;
	}

	public int getMapZoom() {
		return mapZoom;
	}
	public void setMapZoom(int mapZoom) {
		this.mapZoom = mapZoom;
	}
	public Dimension getMapSize() {
		return mapSize;
	}
	public void setMapSize(Dimension mapSize) {
		this.mapSize = mapSize;
	}
	
	
	
	



	public AddressOT getMapAddress() {
		return mapAddress;
	}





	public void setMapAddress(AddressOT mapAddress) {
		this.mapAddress = mapAddress;
	}





	public URL getMapURL() {
		return mapURL;
	}





	public void setMapURL(URL mapURL) {
		this.mapURL = mapURL;
	}





	public void setCoordinates(Point2D coordinates) {
		this.coordinates = coordinates;
	}





	private OperationResult dowloadMapImage(){
		OperationResult result= new OperationResult();
			 if (mapURL == null) {
					ResultMessage res = new ResultMessage(
							InterfaceErrors.ERR_MAP_URL_ERROR);
					result.addResultMessage(OperationResult.RESULT_ERROR, res);
					return result;
			 }
					
			 Image image=ImageUtils.getInstance().loadImage(mapURL);
			 if (image == null) {
					ResultMessage res = new ResultMessage(
							InterfaceErrors.ERR_MAP_IO_ERROR);
					result.addResultMessage(OperationResult.RESULT_ERROR, res);
					return result;
			 }
			setMapImage(image);
			result.addResultMessage(OperationResult.RESULT_OK, new ResultMessage(InterfaceErrors.MSG_MAP_OK));
			return result;
	}
	
	
	
	private OperationResult geocode(){
		OperationResult result= new OperationResult();
		try {
			StringBuffer urlBuffer = new StringBuffer();
			urlBuffer.append(InterfaceExplica.EXPLICA_URL_GOOGLE_GEOCODE);

			urlBuffer.append("?q=");
			urlBuffer.append(mapAddress.getAddress());
			urlBuffer.append("+" + mapAddress.getPostalCode());
			urlBuffer.append("+" + mapAddress.getCity());
			if (mapAddress.getState() != null)
				urlBuffer.append("+" + mapAddress.getState().getName());
			if (mapAddress.getCountry() != null)
				urlBuffer.append("+" + mapAddress.getCountry().getName());
			urlBuffer.append("&key=" + InterfaceExplica.EXPLICA_GOOGLE_APP_KEY);
			urlBuffer.append("&sensor=false");
			urlBuffer.append("&output=xml");

			URL mapRequestURL;

			mapRequestURL = new URL(urlBuffer.toString().replaceAll(" ", "%20"));

			Object contentObj = mapRequestURL.getContent();
			if (contentObj instanceof InputStream) {
				InputStream in = (InputStream) contentObj;
				StringBuffer mapResponse = new StringBuffer();
				int r = in.read();
				while (r >= 0) {
					mapResponse.append((char) r);
					r = in.read();
				}
				in.close();

				System.out.println(mapResponse.toString());

				ByteArrayInputStream bin = new ByteArrayInputStream(mapResponse
						.toString().getBytes());

				InputSource is = new InputSource(bin);

				DocumentBuilderFactory factory = DocumentBuilderFactory
						.newInstance();
				factory.setNamespaceAware(true);
				DocumentBuilder documentBuilder = factory.newDocumentBuilder();
				Document doc = documentBuilder.parse(is);

				bin.close();

				String textoResultadoConsulta = null;
				NodeList listaNodosNS = doc.getElementsByTagNameNS(
						"http://earth.google.com/kml/2.0", "code");

				if (listaNodosNS.getLength() > 0) {
					Node nodo = listaNodosNS.item(0);
					if (nodo instanceof Element)
						textoResultadoConsulta = nodo.getTextContent();
				}
				System.out.println("Resultado geolocalizacion:");
				System.out.println(textoResultadoConsulta);

				if (textoResultadoConsulta == null
						|| textoResultadoConsulta.isEmpty()
						|| !textoResultadoConsulta.equals("200")) {

					ArrayList<String> lParams = new ArrayList<String>();
					lParams.add(textoResultadoConsulta);
					ResultMessage res = ClientContext.getInstance()
							.getMsgCatalog().newResultMessage(
									InterfaceErrors.ERR_MAP_GEOCODING_ERROR);
					res.setLParams(lParams);
					result.addResultMessage(OperationResult.RESULT_ERROR, res);
					return result;
				}

				String textoCoordenadas = null;
				listaNodosNS = doc.getElementsByTagNameNS(
						"http://earth.google.com/kml/2.0", "coordinates");
				if (listaNodosNS.getLength() > 0) {
					Node nodo = listaNodosNS.item(0);
					if (nodo instanceof Element)
						textoCoordenadas = nodo.getTextContent();
				}

				System.out.println("Coordenadras geolocalizacion");
				System.out.println(textoCoordenadas);
				
				
				
				setCoordinates(Converter.getInstance().parseCoordinates(
						textoCoordenadas));
				ResultMessage res = ClientContext.getInstance().getMsgCatalog()
				.newResultMessage(
						InterfaceErrors.MSG_MAP_OK);
				result.addResultMessage(OperationResult.RESULT_OK, res);

			} else {

				ResultMessage res = ClientContext.getInstance().getMsgCatalog()
						.newResultMessage(
								InterfaceErrors.ERR_MAP_GEOCODING_ERROR);
				result.addResultMessage(OperationResult.RESULT_ERROR, res);
			}

		} catch (MalformedURLException me) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_URL_ERROR, me);
			result.addResultMessage(OperationResult.RESULT_ERROR, res);

		} catch (IOException e) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_IO_ERROR, e);
			result.addResultMessage(OperationResult.RESULT_ERROR, res);
		} catch (ParserConfigurationException e) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_PARSE_ERROR, e);
			result.addResultMessage(OperationResult.RESULT_ERROR, res);
		} catch (SAXException e) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_PARSE_ERROR, e);
			result.addResultMessage(OperationResult.RESULT_ERROR, res);
		}
		return result;

	}

	
	

	private OperationResult buildMapURL() {
		OperationResult result=new OperationResult();
		setMapURL(null);
		try {
			StringBuffer urlBuffer = new StringBuffer();
			urlBuffer.append(InterfaceExplica.EXPLICA_URL_GOOGLE_MAPS);
			urlBuffer.append("?center=" + coordinates.getX() + ","
					+ coordinates.getY());
			urlBuffer.append("&zoom=" + getMapZoom());
			urlBuffer.append("&size=" + getMapSize().height+ "x"
					+ getMapSize().width);
			urlBuffer.append("&key=" + InterfaceExplica.EXPLICA_GOOGLE_APP_KEY);
			urlBuffer.append("&sensor=false");
			setMapURL(new URL(urlBuffer.toString().replaceAll(" ", "%20")));

		} catch (MalformedURLException e) {
			ResultMessage res = new ResultMessage(
					InterfaceErrors.ERR_MAP_URL_ERROR, e);
			result.addResultMessage(OperationResult.RESULT_ERROR, res);
		}
		return result;

	}
	
	
	
	

	public OperationResult build(){
		OperationResult result= new OperationResult();
		if (mapAddress==null){
			result.addResultMessage(OperationResult.RESULT_ERROR, new ResultMessage(InterfaceErrors.ERR_MAP_ERROR));
			return result;
		}
		
		result.add(geocode());
		if (result.getResult()==OperationResult.RESULT_OK)
			result.add(buildMapURL());
		if (result.getResult()==OperationResult.RESULT_OK)
			result.add(dowloadMapImage());
		return result;
	}
	
	
	
	
	
	
	
}
